package thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 实现一个交替打印：比如有三个线程分别打印【a】、【b】、【c】，现在想要实现【abcabcabc】的交替打印。
 * 三种实现的相同点：都是通过自身的某些技术，让线程实现交替响应的办法。
 *      synchronized：由于技术受限，所以它只能使用一些全局变量，然后使全局变量交替变化，来控制线程的交替响应。
 *      lock：它可以有多个condition，所以，它的打印，就变成了condition的交替唤醒和阻塞。
 *      park/unpark：park可以阻塞当前线程。unpark可以解锁指定线程。所以这个就是直接控制线程的交替响应。
 *          而且这是唯一一个不需要【锁】就可以实现的方案了。
 */
public class LockTest3 {
    //synchronized的实现方法。
    public static void main2(String[] args) {
        //设置一个初始化的flag和初始化的循环次数。
        SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5);
        new Thread(() -> {
            //判断【要打印a的flag=1，并且打印之后，flag变成=2】
            syncWaitNotify.print(1, 2, "a");
        }).start();
        new Thread(() -> {
            //判断【要打印b的flag=2，并且打印之后，flag变成=3】
            syncWaitNotify.print(2, 3, "b");
        }).start();
        new Thread(() -> {
            //判断【要打印c的flag=3，并且打印之后，flag变成=1】
            syncWaitNotify.print(3, 1, "c");
        }).start();
    }

    //lock的实现方法
    public static void main1(String[] args) throws InterruptedException {

        AwaitSignal awaitSignal = new AwaitSignal(5);
        Condition a = awaitSignal.newCondition();
        Condition b = awaitSignal.newCondition();
        Condition c = awaitSignal.newCondition();

        new Thread(() -> {
            //将a条件暂停，之后打印了【a】，再放开b条件的。
            awaitSignal.print("a", a, b);
        }).start();

        new Thread(() -> {
            //将b条件暂停，之后打印了【b】，再放开c条件的。
            awaitSignal.print("b", b, c);
        }).start();

        new Thread(() -> {
            //将c条件暂停，之后打印了【c】，再放开a条件的。
            awaitSignal.print("c", c, a);
        }).start();

        Thread.sleep(1000);

        awaitSignal.lock();
        try {
            System.out.println("开始...");
            //将a条件放开，这个时候才开始打印。
            a.signal();
        } finally {
            awaitSignal.unlock();
        }

    }

    static Thread t1;
    static Thread t2;
    static Thread t3;
    //park/unpark的实现方法
    public static void main(String[] args) {
        ParkUnpark pu = new ParkUnpark(5);

        t1 = new Thread(() -> {
            pu.print("a", t2);
        });
        t2 = new Thread(() -> {
            pu.print("b", t3);
        });
        t3 = new Thread(() -> {
            pu.print("c", t1);
        });
        t1.start();
        t2.start();
        t3.start();
        //放行t1线程。
        LockSupport.unpark(t1);
    }

}


//synchronized 版本
class SyncWaitNotify {
    private int flag;
    private int loopNumber;

    public SyncWaitNotify(int flag, int loopNumber) {
        this.flag = flag;
        this.loopNumber = loopNumber;
    }

    //synchronized就是通过 flag的交替变化实现交替打印的。
    public void print(int waitFlag, int nextFlag, String str) {
        for (int i = 0; i < loopNumber; i++) {
            //锁住。
            synchronized (this) {
                //通过 flag 的交替变换实现，交替打印。
                while (this.flag != waitFlag) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.print(str);
                flag = nextFlag;
                this.notifyAll();
            }
        }
    }
}


//lock 版本
class AwaitSignal extends ReentrantLock{
    private int loopNumber;

    public AwaitSignal(int loopNumber) {
        this.loopNumber = loopNumber;
    }

    // 参数1 打印内容， 参数2 进入哪一间休息室, 参数3 下一间休息室
    //实际上：lock版本就是通过控制 condition 的交替唤醒，来实现打印的。
    public void print(String str, Condition current, Condition next) {
        for (int i = 0; i < loopNumber; i++) {
            lock();
            try {
                //将当前打印的线程暂停。
                current.await();
                //打印当前的字符。
                System.out.print(str);
                //放开下一个打印的线程
                next.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                unlock();
            }
        }
    }
}


//park/unpark的版本
class ParkUnpark {

    private int loopNumber;

    public ParkUnpark(int loopNumber) {
        this.loopNumber = loopNumber;
    }

    public void print(String str, Thread next) {
        for (int i = 0; i < loopNumber; i++) {
            //暂停当前线程
            LockSupport.park();
            //打印该字符串
            System.out.print(str);
            //唤醒下个线程。
            LockSupport.unpark(next);
        }
    }

}
